package org.fast.project.web.advice;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.ValidationException;
import lombok.extern.slf4j.Slf4j;

import org.fast.project.common.exception.AbstractBusinessException;
import org.fast.project.web.vo.CommonResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.servlet.NoHandlerFoundException;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author : lilith·bristol
 * @program: spring-web-archetype-exception
 * @description: 基础全局异常处理类 所有基础的业务异常都需要在此做出处理
 * 以返回友好的错误信息到前台
 * @date : 2019-04-23 01:05
 **/
@SuppressWarnings("rawtypes")
@RestControllerAdvice
@Slf4j
public class GlobalExceptionResolverAdvice {
    /**
     * @param request, response, t
     *
     * @return fast.project.vo.CommonResponse
     *
     * @author lilith·bristol
     * @description 全局非业务异常处理器 用于处理全局非业务异常（比如空指针） 避免内部错误返回到请求端
     * @date 2:47 2020/4/19
     */
    @ExceptionHandler(value = Throwable.class)
    @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
    public CommonResponse globalSysExceptionHandler(HttpServletRequest request, HttpServletResponse response,
        Throwable t) {
        log.error("请求号{} 程序运行出现系统级异常{} ", RequestContextUtil.getRequestId(), t.getMessage(), t);
        return CommonResponse.createFailureResponse("服务内部错误,请与管理员联系", "[Server].[InternalError]");
    }

    /**
     * @param request, response, adSysException
     *
     * @return 服务器内部错误500
     *
     * @author lilith·bristol
     * @description 业务类异常捕获处理
     * @date 1:50 2019/4/23
     */
    @ExceptionHandler(value = AbstractBusinessException.class)
    @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
    public CommonResponse<AbstractBusinessException> abstractBusinessExceptionHandler(HttpServletRequest request,
        HttpServletResponse response, AbstractBusinessException adSysException) {
        log.error("程序运行出现业务异常！", adSysException);
        return CommonResponse.createFailureResponse(adSysException);
    }

    /**
     * 路由层方法参数校验异常收集器
     * 此异常处理器的主要作用是链式处理路由层请求参数未通过Bean-Validation库校验所产生异常信息
     *
     * @param req 请求
     * @param e   方法异常
     *
     * @return CommonResponse
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public CommonResponse actionMethodValidExceptionHandler(HttpServletRequest req, MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        List<String> validateResultList = bindingResult.getFieldErrors().stream().map(x -> {
            String field = x.getField();
            String value = "";
            if (null != x.getRejectedValue()) {
                value = x.getRejectedValue().toString();
            }
            String message = x.getDefaultMessage();
            return String.format("请求参数[%s]的值[%s]不正确:[%s]", field, value, message);
        }).collect(Collectors.toList());
        return CommonResponse.createFailureResponse(String.join(",", validateResultList))
            .setMessageCode("[Server].[RequestParamInvalid]");
    }

    /**
     * @param request, response, ex}
     *
     * @return {@link CommonResponse<String>}
     *
     * @author lilith·bristol
     * @description 方法参数校验异常 捕获处理
     * 此异常处理器的主要作用是链式处理请求参数Java类未通过Bean-Validation库校验所产生异常信息
     * @date 18:26 2019/5/1
     */
    @ExceptionHandler(value = ConstraintViolationException.class)
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public CommonResponse methodParamValidExceptionHandler(HttpServletRequest request, HttpServletResponse response,
        ValidationException ex) {
        if (ex instanceof ConstraintViolation) {
            StringBuilder sb = new StringBuilder();
            for (ConstraintViolation<?> exConstraintViolation : ((ConstraintViolationException) ex).getConstraintViolations()) {
                sb.append(exConstraintViolation.getMessage());
            }
            log.debug("请求参数错误 未通过校验 {}", sb);
            return CommonResponse.createFailureResponse("请求参数中包含一个或多个不正确的参数:" + sb,
                "[Server].[RequestParamInvalid]");
        } else {
            log.debug("请求参数错误 未通过校验 !", ex);
        }
        return CommonResponse.createFailureResponse("请求参数中包含一个或多个不正确的参数:" + ex.getMessage(),
            "[Server].[RequestParamInvalid]");
    }

    /**
     * @param request, rep, e
     *
     * @return 页面未找到404
     *
     * @author lilith·bristol
     * @description 页面或视图处理器异常处理
     * @date 1:51 2019/4/23
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(code = HttpStatus.NOT_FOUND)
    public CommonResponse pageNotFound(HttpServletRequest request, HttpServletResponse rep, NoHandlerFoundException e) {
        log.info("客户{}请求的页面或视图处理器未找到 发生异常{}", request.getRemoteUser(), e.getMessage(),e);
        return CommonResponse.createFailureResponse("请求的页面或视图处理器未找到", "[Server].[PageNotFound]");
    }

    /**
     * @param request, rep, e
     *
     * @return 服务器不接收406
     *
     * @author lilith·bristol
     * @description 上传文件失败异常处理
     * @date 1:51 2019/4/23
     */
    @ExceptionHandler(MultipartException.class)
    @ResponseStatus(code = HttpStatus.NOT_ACCEPTABLE)
    public CommonResponse uploadFileException(HttpServletRequest request, MultipartException e) {
        log.error("客户{}上传文件失败 发生异常{}", request.getRemoteUser(), e.getMessage(), e);
        return CommonResponse.createFailureResponse("文件上传失败 请检查文件上传设置", "[Server].[UploadFileFail]");
    }

    /**
     * @param request, rep, e
     *
     * @return 不支持的请求体类型415
     *
     * @author lilith·bristol
     * @description 请求内容体 RequestBody 无法映射至接受方法内容体(SpringWeb框架 处理方法中标注@RequestBody的参数)
     * @date 1:51 2019/4/23
     * @see org.springframework.web.bind.annotation.RequestBody
     */
    @ExceptionHandler(value = {HttpMessageNotReadableException.class})
    @ResponseStatus(code = HttpStatus.UNSUPPORTED_MEDIA_TYPE)
    public CommonResponse httpMessageErrorHandler(HttpServletRequest request, Exception e) {
        log.debug("客户{}发送请求时 请求内容体无法映射至接受方法的内容体 发生异常{}", request.getRemoteUser(),
            e.getMessage(), e);
        return CommonResponse.createFailureResponse("请求内容体无法映射至接受方法的内容体",
            "[Server].[UnSupportMediaType]");
    }

    /**
     * @param request, rep, e
     *
     * @return 不允许的请求方法405
     *
     * @author lilith·bristol
     * @description 接受的HTTP请求 与响应方法标注的接受请求类型(SpringWeb框架 处理方法上标注的@RequestMapping 或 @GetMapping)不匹配
     * @date 1:51 2019/4/23
     * @see org.springframework.web.bind.annotation.RequestMapping
     */
    @ExceptionHandler(value = {HttpRequestMethodNotSupportedException.class})
    @ResponseStatus(code = HttpStatus.METHOD_NOT_ALLOWED)
    public CommonResponse httpRequestErrorHandler(HttpServletRequest request, Exception e) {
        log.debug("客户{}发送请求时 HTTP请求方式错误 请替换为正确的请求方式 发生异常{}", request.getRemoteUser(),
            e.getMessage(), e);
        return CommonResponse.createFailureResponse("发送请求时 HTTP请求方式错误 请替换为正确的请求方式",
            "[Server].[MethodNotAllowed]");
    }

    /**
     * @param request, rep, e
     *
     * @return 错误的请求400
     *
     * @author lilith·bristol
     * @description 接受的HTTP请求中的参数列表 缺少必须的请求参数 (SpringWeb框架 处理方法中标注@RequestParam的参数)
     * @date 1:51 2019/4/23
     * @see org.springframework.web.bind.annotation.RequestParam
     */
    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public CommonResponse missingServletRequestParameterException(HttpServletRequest request, Exception e) {
        log.debug("客户{}发送请求时 缺少必须的请求参数 请检查参数列表 发生异常{}", request.getRemoteUser(),
            e.getMessage(), e);
        return CommonResponse.createFailureResponse("发送请求时 缺少必须的请求参数 请检查参数列表",
            "[Server].[BadRequest]");
    }
}
